package user

import (
	"context"
	"github.com/golang-jwt/jwt"
	v1 "go-skeleton/api/v1/service/user"
	userBiz "go-skeleton/app/v1/service/internal/biz/user"
	"go-skeleton/app/v1/service/internal/data/ent"
	"google.golang.org/protobuf/types/known/emptypb"
	"time"
)

// UserService is a user service.
type UserService struct {
	v1.UnimplementedUserServer

	uc *userBiz.UserBiz
}

func NewUserService(uc *userBiz.UserBiz) *UserService {
	return &UserService{uc: uc}
}

func (s *UserService) Register(ctx context.Context, in *v1.RegisterRequest) (resp *emptypb.Empty, err error) {
	err = s.uc.VerifyCode(ctx, in.Phone, in.VerifyCode)
	if err != nil {
		return nil, userBiz.ErrVerifyCodeErr
	}

	u, err := s.uc.FindByPhone(ctx, in.Phone)
	if err != nil && err != userBiz.ErrUserNotFound {
		return nil, userBiz.ErrSystemErr
	}

	if u != nil {
		return nil, userBiz.ErrUserRegistered
	}

	_, err = s.uc.CreateUser(ctx, &ent.User{
		Phone:    in.Phone,
		Password: in.Password,
	})
	if err != nil {
		return
	}

	return
}

func (s *UserService) Login(ctx context.Context, in *v1.LoginRequest) (resp *v1.LoginReply, err error) {
	resp = &v1.LoginReply{}
	user, err := s.uc.FindByPhone(ctx, in.Phone)
	if err != nil {
		return
	}

	switch in.LoginType {
	case v1.LoginType_PassWord:
		err = s.uc.VerifyPassword(ctx, &ent.User{
			Phone:    in.Phone,
			Password: in.Password,
		})
	case v1.LoginType_VerifyCode:
		err = s.uc.VerifyCode(ctx, in.Phone, in.VerifyCode)
	}

	if err != nil {
		return
	}

	// generate token
	expire := time.Now().Add(time.Hour * 24).Unix()
	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"user_id": user.ID,
		"expire": jwt.StandardClaims{
			ExpiresAt: expire,
		},
	})
	signedString, err := claims.SignedString([]byte(""))
	resp = &v1.LoginReply{
		Token:  signedString,
		Expire: expire,
	}
	return
}

func (s *UserService) SendCode(ctx context.Context, in *v1.SendCodeRequest) (resp *emptypb.Empty, err error) {
	err = s.uc.SendCode(ctx, in.Phone)
	return
}

func (s *UserService) GetUserInfo(ctx context.Context, in *v1.GetUserInfoRequest) (resp *v1.GetUserInfoReply, err error) {
	resp = &v1.GetUserInfoReply{}
	user, err := s.uc.FindByID(ctx, in.Id)
	if err != nil {
		return
	}

	resp = &v1.GetUserInfoReply{
		User: &v1.UserInfo{
			Id:       user.ID,
			NickName: user.NickName,
			Avatar:   user.Avatar,
		},
	}
	return
}
